#include<bits/stdc++.h>

using namespace std;

using ll = long long;
#define endl '\n'
const int maxn = 510;
int F[maxn];

void Show(int n) {
    for (int i = 1; i <= n; i++) {
        cout << i << "<-" << F[i] << endl;
    }
}

/**
 * 并查集的初始化:把数组F[i](1<=i <=n )设置为F[i]=i
 * @param n
 */
void Init(int n) {
    for (int i = 1; i <= n; i++)F[i] = i;
}

/**
 * 并查集找根节点：寻找x所在的那个树的树根
 * @param x
 * @return
 */
int Find(int x) {
    if (x == F[x]) {
        return x;//说明x就是树根
    } else {
        return F[x] = Find(F[x]);//递归的寻找 F[x]的根,路径压缩
    }
}
/**
 * 合并x,y所在的子树为一棵树
 * @param x
 * @param y
 * @return
 */
bool Union(int x, int y) {
    int fx = Find(x);
    int fy = Find(y);
    if (fx == fy)return false;//不用合并,xy已经在一棵树上
    F[fy] = fx;//把 fy所在的子树都合并到fx这个树上，fx是合并后的树根
    return true;
}

/**
 * 计算连通分支数，（森林里树个数量）
 * @param n
 * @return
 */
int Count(int n) {
    int cnt = 0;
    for (int i = 1; i <= n; i++) {
        if (F[i] == i) {
            ++cnt;
        }
    }
    return cnt;
}

int n, m = 0;

struct Edge {
    int x, y, w;

    bool operator<(const Edge &that) const {
        return this->w < that.w;
    }
} E[1000];

int main() {
    //freopen("test.in", "r", stdin);
    cin >> n;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++) {
            int t;
            cin >> t;
            if (i == j || t == 0)continue;
            E[m++] = {i, j, t};
        }
    sort(E, E + m);
    Init(n);
    int tot = 0;
    int cnt = 0;
    for (int i = 0; i < m; i++) {

        if (!Union(E[i].x, E[i].y))continue;//选择E[i]边会导致一个环路
        tot += E[i].w;
        cnt++;
        if (cnt == n - 1)break;
    }

    if (cnt < n - 1)cout << -1 << endl;
    else cout << tot << endl;

    return 0;
}